package com.spark.core;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.spark.actions.SelectAction;
import com.spark.util.MemcachedHelper;
import com.spark.util.SourceFileScanner;

import freemarker.template.Template;


@SuppressWarnings("unchecked")
public class SparkDispatcher extends SparkServlet {

	private static final long serialVersionUID = -743284205522781195L;
	
	private static final String ACTION_CLASS = "class";
	private static final String ACTION_METHOD = "method";
	
	private static final String ACTIONS_ACTION_CLASS = "selectAction";
	private static final String ACTIONS_ACTION = "action";
	
	public static final String SPARK_CONTEXT = "__context";
	public static final String SPARK_FORWARD = "__forward";
	
	private String WEB_ROOT_PATH;

	public void destroy() {
		SourceFileScanner.getInstance().shutDown();
	}
	
	
	/**
	 * 
	 * (1) 获取SparkTemplateLoader的唯一实例
	 * (2) 通过FileUtils.readFileToString读取文本,然后XPP解析actions
	 * (3) executeActions()
	 * (4) FreeMarker处理screen的内容,通过XMapHandler解析成XMap,同时过滤掉actions标签部分
	 * 
	 */
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		initRequestAndResponse(request, response);
		String name = getServiceName(request);
		SparkTemplateLoader loader = SparkTemplateLoader.getInstance();
		SparkTemplate template;
		String str = null;
		try {
			template = loader.load(new File(WEB_ROOT_PATH, name));
			if(template.isCache() && CacheManager.getInstance().getScreenHTML(template.getFilePath()) != null){
				str = CacheManager.getInstance().getScreenHTML(template.getFilePath());
			}else {
				SparkContext context = (SparkContext)request.getAttribute(SPARK_CONTEXT);
				if(context == null) {
					context = new SparkContext(SparkConfiguration.getInstance());
					request.setAttribute(SPARK_CONTEXT, context);
				}
				context.setTemplate(template);
				initContext(context,request,response);
				if(template.checkSession() && context.getSession().isEmpty()){
					RequestDispatcher dispatcher = request.getRequestDispatcher("/noSession.screen");
					dispatcher.forward(request,response);
					return;
				}
				executeActions(template, context);
				Boolean isForward = (Boolean) context.get(SparkDispatcher.SPARK_FORWARD);
				if(isForward!=null && isForward == true){
					context.put(SPARK_FORWARD, false);
				}else{
					XMap xmap = executeTemplate(template, context);
					str = SparkBuilder.build(xmap, context);
					if(template.isCache()){
						CacheManager.getInstance().setScreenHTML(template.getFilePath(), str);
					}
				}
			} 
			if(str!=null) response.getWriter().write(str);
		} catch (FileNotFoundException fnfe){
			fnfe.printStackTrace();
			RequestDispatcher dispatcher = request.getRequestDispatcher("/404.screen");
			dispatcher.forward(request, response);
		} catch (Exception e) {
			e.printStackTrace();
			throw new ServletException(e);
//		} finally {
//			response.getWriter().close();//会导致异常无法捕获			
		}
	}
	
	protected void executeActions(SparkTemplate template,SparkContext context) throws InstantiationException, IllegalAccessException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
		if(template != null){
			List list = template.getActions();
			if(list!=null){
				Iterator it = list.iterator();
				while(it.hasNext()){
					XMap amap = (XMap)it.next();
					String name = amap.getName();
					String nameSpace = amap.getNamespaceURI();
					if(ACTIONS_ACTION.equalsIgnoreCase(name)){
						String clazz = amap.getString(ACTION_CLASS,"");
						String method = amap.getString(ACTION_METHOD,"");
						if(!"".equals(clazz) && !"".equals(method)) {
							Class cls = Class.forName(clazz);
							//TODO:根据cls缓存这些对象
							Object act = cls.newInstance();
							Method m = cls.getMethod(method,new Class[]{SparkContext.class}); 
							Object result = m.invoke(act,new Object[]{context});
							if(result!=null && result.equals(false)){
								break;
							}
						}
					} else if(SelectAction.ACTIONS_SELECT_OBJECT.equalsIgnoreCase(name) || SelectAction.ACTIONS_SELECT_MAP.equalsIgnoreCase(name) || SelectAction.ACTIONS_SELECT_LIST.equalsIgnoreCase(name) ){
						String method = name;
						String actionNameSpace = amap.getString("namespace");
						String actionId = amap.getString(SelectAction.SELECT_ID);
						String result = amap.getString(SelectAction.SELECT_RESULT);
						String parameter = amap.getString(SelectAction.SELECT_PARAMETER);
						SparkConfiguration configuratin = context.getSparkConfiguration();
						Object act = configuratin.getObject(nameSpace, ACTIONS_ACTION_CLASS);
						Method m = act.getClass().getMethod(method,new Class[]{SparkContext.class,String.class,String.class,String.class,String.class}); 
						m.invoke(act,new Object[]{context,actionNameSpace,actionId,result,parameter});						
					}
				}
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	protected XMap executeTemplate(SparkTemplate template,SparkContext context) throws Exception {
		Template t = CacheManager.getInstance().getTemplate(template,context);
		String result = "";
		StringWriter out = null;
		try {
			out = new StringWriter();
			Map map = context.getFreeMarkerModel();
			t.process(map, out);
			out.flush();
			result =  out.toString();
		} finally {
			if (out != null)
				out.close();
		}
		return XMapParser.parseReader(new StringReader(result), new ScreenHandler(template));
	}
	
	
	public void init(ServletConfig config) throws ServletException {
		try {
			super.init(config);
			printFrameworkInfo(config.getServletContext());
			initConfiguration(config.getServletContext());
			startScanner();
			MemcachedHelper.init();
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(1);
		}
	}

	private String getServiceName(HttpServletRequest request) {
		String service_name = request.getServletPath();
		if (service_name.charAt(0) == '/')
			service_name = service_name.substring(1);
		return service_name;
	}

	private void initConfiguration(ServletContext servletContext) throws Exception {
		WEB_ROOT_PATH = servletContext.getRealPath("/WEB-INF");
		SparkConfiguration instance = SparkConfiguration.getInstance();
		instance.init(WEB_ROOT_PATH);
	}
	
	private void startScanner(){
		SourceFileScanner.init(SparkConfiguration.getInstance());
		SourceFileScanner.getInstance().startUp();
	}

	private void printFrameworkInfo(ServletContext context) throws Exception {
		StringBuffer sb = new StringBuffer(), line = new StringBuffer();
		sb.append("* ").append("Spark 1.0");
		sb.append("  |  ").append(context.getResource("/").toExternalForm());
		sb.append("  |  ").append(context.getServletContextName());
		sb.append(" *");
		int len = sb.length();
		for (int i = 0; i < len; i++) {
			line.append("*");
		}
		System.out.println(line.toString());
		System.out.println(sb.toString());
		System.out.println(line.toString());
	}

}
